﻿#include "StdAfx.h"

namespace DuiLib {
    IMPLEMENT_DUICONTROL(CControlUI)

    CControlUI::CControlUI()
        : m_pManager(NULL),
          m_pParent(NULL),
          m_bUpdateNeeded(true),
          m_bMenuUsed(false),
          m_bVisible(true),
          m_bInternVisible(true),
          m_bFocused(false),
          m_bEnabled(true),
          m_bMouseEnabled(true),
          m_bKeyboardEnabled(true),
          m_bFloat(false),
          m_uFloatAlign(0),
          m_bSetPos(false),
          m_bDragEnabled(false),
          m_bDropEnabled(false),
          m_bResourceText(false),
          m_chShortcut('\0'),
          m_pTag(NULL),
          m_dwBackColor(0),
          m_dwBackColor2(0),
          m_dwBackColor3(0),
          m_dwForeColor(0),
          m_dwBorderColor(0),
          m_dwFocusBorderColor(0),
          m_bColorHSL(false),
          m_nBorderStyle(PS_SOLID),
          m_nTooltipWidth(300),
          m_wCursor(0),
          m_instance(NULL) {
        m_cXY.cx = m_cXY.cy = 0;
        m_cxyFixed.cx = m_cxyFixed.cy = 0;
        m_cxyMin.cx = m_cxyMin.cy = 0;
        m_cxyMax.cx = m_cxyMax.cy = 9999;
        m_cxyBorderRound.cx = m_cxyBorderRound.cy = 0;

        ::ZeroMemory(&m_rcPadding, sizeof(RECT));
        ::ZeroMemory(&m_rcItem, sizeof(RECT));
        ::ZeroMemory(&m_rcPaint, sizeof(RECT));
        ::ZeroMemory(&m_rcBorderSize, sizeof(RECT));
        m_piFloatPercent.left = m_piFloatPercent.top = m_piFloatPercent.right = m_piFloatPercent.bottom = 0.0f;
    }

    CControlUI::~CControlUI() {
        if( m_OnDestroyDelegate ) m_OnDestroyDelegate(this);

        RemoveAllCustomAttribute();

        if( m_pManager != NULL )
            m_pManager->ReapObjects(this);
    }

    CDuiString CControlUI::GetName() const {
        return m_sName;
    }

    void CControlUI::SetName(LPCTSTR pstrName) {
        m_sName = pstrName;
    }

    LPVOID CControlUI::GetInterface(LPCTSTR pstrName) {
        if( _tcsicmp(pstrName, DUI_CTR_CONTROL) == 0 ) return this;

        return NULL;
    }

    LPCTSTR CControlUI::GetClass() const {
        return DUI_CTR_CONTROL;
    }

    UINT CControlUI::GetControlFlags() const {
        return 0;
    }

    bool CControlUI::Activate() {
        if( !IsVisible() )
            return false;

        if( !IsEnabled() )
            return false;

        return true;
    }

    CPaintManagerUI *CControlUI::GetManager() const {
        return m_pManager;
    }

    void CControlUI::SetManager(CPaintManagerUI *pManager, CControlUI *pParent, bool bInit) {
        m_pManager = pManager;
        m_pParent = pParent;

        if( bInit && m_pParent ) Init();
    }

    CControlUI *CControlUI::GetParent() const {
        return m_pParent;
    }

    bool CControlUI::SetTimer(UINT nTimerID, UINT nElapse) {
        if(m_pManager == NULL) return false;

        return m_pManager->SetTimer(this, nTimerID, nElapse);
    }

    void CControlUI::KillTimer(UINT nTimerID) {
        if(m_pManager == NULL) return;

        m_pManager->KillTimer(this, nTimerID);
    }

    CDuiString CControlUI::GetText() const {
        if (!IsResourceText()) return m_sText;

        return CResourceManager::GetInstance()->GetText(m_sText);
    }

    void CControlUI::SetText(LPCTSTR pstrText) {
        if( m_sText == pstrText ) return;

        m_sText = pstrText;
        // 解析xml换行符
        m_sText.Replace(_T("{\\n}"), _T("\n"));
        Invalidate();
    }

    bool CControlUI::IsResourceText() const {
        return m_bResourceText;
    }

    void CControlUI::SetResourceText(bool bResource) {
        if( m_bResourceText == bResource ) return;

        m_bResourceText = bResource;
        Invalidate();
    }

    bool CControlUI::IsDragEnabled() const {
        return m_bDragEnabled;
    }

    void CControlUI::SetDragEnable(bool bDrag) {
        m_bDragEnabled = bDrag;
    }

    bool CControlUI::IsDropEnabled() const {
        return m_bDropEnabled;
    }

    void CControlUI::SetDropEnable(bool bDrop) {
        m_bDropEnabled = bDrop;
    }

    LPCTSTR CControlUI::GetGradient() {
        return m_sGradient;
    }

    void CControlUI::SetGradient(LPCTSTR pStrImage) {
        if( m_sGradient == pStrImage ) return;

        m_sGradient = pStrImage;
        Invalidate();
    }

    DWORD CControlUI::GetBkColor() const {
        return m_dwBackColor;
    }

    void CControlUI::SetBkColor(DWORD dwBackColor) {
        if( m_dwBackColor == dwBackColor ) return;

        m_dwBackColor = dwBackColor;
        Invalidate();
    }

    DWORD CControlUI::GetBkColor2() const {
        return m_dwBackColor2;
    }

    void CControlUI::SetBkColor2(DWORD dwBackColor) {
        if( m_dwBackColor2 == dwBackColor ) return;

        m_dwBackColor2 = dwBackColor;
        Invalidate();
    }

    DWORD CControlUI::GetBkColor3() const {
        return m_dwBackColor3;
    }

    void CControlUI::SetBkColor3(DWORD dwBackColor) {
        if( m_dwBackColor3 == dwBackColor ) return;

        m_dwBackColor3 = dwBackColor;
        Invalidate();
    }

    DWORD CControlUI::GetForeColor() const {
        return m_dwForeColor;
    }

    void CControlUI::SetForeColor(DWORD dwForeColor) {
        if( m_dwForeColor == dwForeColor ) return;

        m_dwForeColor = dwForeColor;
        Invalidate();
    }

    LPCTSTR CControlUI::GetBkImage() {
        return m_sBkImage;
    }

    void CControlUI::SetBkImage(LPCTSTR pStrImage) {
        if(m_pManager) m_pManager->RemoveImage(pStrImage);

        if( m_sBkImage == pStrImage ) return;

        m_sBkImage = pStrImage;
        Invalidate();
    }

    LPCTSTR CControlUI::GetForeImage() const {
        return m_sForeImage;
    }

    void CControlUI::SetForeImage(LPCTSTR pStrImage) {
        if( m_sForeImage == pStrImage ) return;

        m_sForeImage = pStrImage;
        Invalidate();
    }

    DWORD CControlUI::GetBorderColor() const {
        return m_dwBorderColor;
    }

    void CControlUI::SetBorderColor(DWORD dwBorderColor) {
        if( m_dwBorderColor == dwBorderColor ) return;

        m_dwBorderColor = dwBorderColor;
        Invalidate();
    }

    DWORD CControlUI::GetFocusBorderColor() const {
        return m_dwFocusBorderColor;
    }

    void CControlUI::SetFocusBorderColor(DWORD dwBorderColor) {
        if( m_dwFocusBorderColor == dwBorderColor ) return;

        m_dwFocusBorderColor = dwBorderColor;
        Invalidate();
    }

    bool CControlUI::IsColorHSL() const {
        return m_bColorHSL;
    }

    void CControlUI::SetColorHSL(bool bColorHSL) {
        if( m_bColorHSL == bColorHSL ) return;

        m_bColorHSL = bColorHSL;
        Invalidate();
    }

    CDuiRect CControlUI::GetBorderSize() const {
        if (!m_pManager)
            return m_rcBorderSize;
        return m_pManager->GetDPIObj()->Scale(m_rcBorderSize);

    }

    void CControlUI::SetBorderSize( CDuiRect rc ) {
        m_rcBorderSize = rc;
        Invalidate();
    }

    SIZE CControlUI::GetBorderRound() const {
        if (!m_pManager)
            return m_cxyBorderRound;
        return m_pManager->GetDPIObj()->Scale(m_cxyBorderRound);

    }

    void CControlUI::SetBorderRound(SIZE cxyRound) {
        m_cxyBorderRound = cxyRound;
        Invalidate();
    }

    bool CControlUI::DrawImage(HDC hDC, LPCTSTR pStrImage, LPCTSTR pStrModify) {
        return CRenderEngine::DrawImageString(hDC, m_pManager, m_rcItem, m_rcPaint, pStrImage, pStrModify, m_instance);
    }

    const RECT &CControlUI::GetPos() const {
        return m_rcItem;
    }

    RECT CControlUI::GetRelativePos() const {
        CControlUI *pParent = GetParent();

        if( pParent != NULL ) {
            RECT rcParentPos = pParent->GetPos();
            CDuiRect rcRelativePos(m_rcItem);
            rcRelativePos.Offset(-rcParentPos.left, -rcParentPos.top);
            return rcRelativePos;
        } else {
            return CDuiRect(0, 0, 0, 0);
        }
    }

    RECT CControlUI::GetClientPos() const {
        return m_rcItem;
    }

    void CControlUI::SetPos(RECT rc, bool bNeedInvalidate) {
        if( rc.right < rc.left )
            rc.right = rc.left;

        if( rc.bottom < rc.top )
            rc.bottom = rc.top;

        CDuiRect invalidateRc = m_rcItem;

        if( ::IsRectEmpty(&invalidateRc) )
            invalidateRc = rc;

        m_rcItem = rc;

        if( m_pManager == NULL )
            return;

        if( !m_bSetPos ) {
            m_bSetPos = true;

            if( m_OnSizeDelegate )
                m_OnSizeDelegate(this);

            m_bSetPos = false;
        }

        m_bUpdateNeeded = false;

        if( bNeedInvalidate && IsVisible() ) {
            invalidateRc.Join(m_rcItem);
            CControlUI *pParent = this;
            RECT rcTemp;
            RECT rcParent;

            while( (pParent = pParent->GetParent()) ) {
                if( !pParent->IsVisible() )
                    return;

                rcTemp = invalidateRc;
                rcParent = pParent->GetPos();

                if( !::IntersectRect(&invalidateRc, &rcTemp, &rcParent) )
                    return;
            }

            m_pManager->Invalidate(invalidateRc);
        }
    }

    void CControlUI::Move(SIZE szOffset, bool bNeedInvalidate) {
        m_cXY.cx += szOffset.cx;
        m_cXY.cy += szOffset.cy;
        NeedParentUpdate();
    }

    int CControlUI::GetWidth() const {
        return m_rcItem.right - m_rcItem.left;
    }

    int CControlUI::GetHeight() const {
        return m_rcItem.bottom - m_rcItem.top;
    }

    int CControlUI::GetX() const {
        return m_rcItem.left;
    }

    int CControlUI::GetY() const {
        return m_rcItem.top;
    }

    RECT CControlUI::GetPadding() const {
        if (!m_pManager)
            return m_rcPadding;
        return m_pManager->GetDPIObj()->Scale(m_rcPadding);
    }

    void CControlUI::SetPadding(RECT rcPadding) {
        m_rcPadding = rcPadding;
        NeedParentUpdate();
    }

    SIZE CControlUI::GetFixedXY() const {
        if (!m_pManager)
            return m_cXY;
        return m_pManager->GetDPIObj()->Scale(m_cXY);
    }

    void CControlUI::SetFixedXY(SIZE szXY) {
        m_cXY.cx = szXY.cx;
        m_cXY.cy = szXY.cy;
        NeedParentUpdate();
    }

    int CControlUI::GetFixedWidth() const {
        if (!m_pManager)
            return m_cxyFixed.cx;
        return m_pManager->GetDPIObj()->Scale(m_cxyFixed.cx);
    }

    void CControlUI::SetFixedWidth(int cx) {
        if( cx < 0 ) return;

        m_cxyFixed.cx = cx;
        NeedParentUpdate();
    }

    int CControlUI::GetFixedHeight() const {
        if (!m_pManager)
            return m_cxyFixed.cy;
        return m_pManager->GetDPIObj()->Scale(m_cxyFixed.cy);
    }

    void CControlUI::SetFixedHeight(int cy) {
        if( cy < 0 )
            return;

        m_cxyFixed.cy = cy;
        NeedParentUpdate();
    }

    int CControlUI::GetMinWidth() const {
        if (!m_pManager)
            return m_cxyMin.cx;
        return m_pManager->GetDPIObj()->Scale(m_cxyMin.cx);
    }

    void CControlUI::SetMinWidth(int cx) {
        if( m_cxyMin.cx == cx )
            return;

        if( cx < 0 )
            return;

        m_cxyMin.cx = cx;
        NeedParentUpdate();
    }

    int CControlUI::GetMaxWidth() const {
        if (!m_pManager)
            return m_cxyMax.cx;
        return m_pManager->GetDPIObj()->Scale(m_cxyMax.cx);
    }

    void CControlUI::SetMaxWidth(int cx) {
        if( m_cxyMax.cx == cx )
            return;

        if( cx < 0 )
            return;

        m_cxyMax.cx = cx;
        NeedParentUpdate();
    }

    int CControlUI::GetMinHeight() const {
        if (!m_pManager)
            return m_cxyMin.cy;
        return m_pManager->GetDPIObj()->Scale(m_cxyMin.cy);
    }

    void CControlUI::SetMinHeight(int cy) {
        if( m_cxyMin.cy == cy )
            return;

        if( cy < 0 )
            return;

        m_cxyMin.cy = cy;
        NeedParentUpdate();
    }

    int CControlUI::GetMaxHeight() const {
        if (!m_pManager)
            return m_cxyMax.cy;
        return m_pManager->GetDPIObj()->Scale(m_cxyMax.cy);
    }

    void CControlUI::SetMaxHeight(int cy) {
        if( m_cxyMax.cy == cy )
            return;

        if( cy < 0 )
            return;

        m_cxyMax.cy = cy;
        NeedParentUpdate();
    }

    TPercentInfo CControlUI::GetFloatPercent() const {
        return m_piFloatPercent;
    }

    void CControlUI::SetFloatPercent(TPercentInfo piFloatPercent) {
        m_piFloatPercent = piFloatPercent;
        NeedParentUpdate();
    }

    void CControlUI::SetFloatAlign(UINT uAlign) {
        m_uFloatAlign = uAlign;
        NeedParentUpdate();
    }

    UINT CControlUI::GetFloatAlign() const {
        return m_uFloatAlign;
    }

    CDuiString CControlUI::GetToolTip() const {
        if (!IsResourceText())
            return m_sToolTip;

        return CResourceManager::GetInstance()->GetText(m_sToolTip);
    }

    void CControlUI::SetToolTip(LPCTSTR pstrText) {
        CDuiString strTemp(pstrText);
        strTemp.Replace(_T("<n>"), _T("\r\n"));
        m_sToolTip = strTemp;
    }

    void CControlUI::SetToolTipWidth( int nWidth ) {
        m_nTooltipWidth = nWidth;
    }

    int CControlUI::GetToolTipWidth( void ) {
        if (!m_pManager)
            return m_nTooltipWidth;
        return m_pManager->GetDPIObj()->Scale(m_nTooltipWidth);
    }

    WORD CControlUI::GetCursor() {
        return m_wCursor;
    }

    void CControlUI::SetCursor(WORD wCursor) {
        m_wCursor = wCursor;
        Invalidate();
    }

    TCHAR CControlUI::GetShortcut() const {
        return m_chShortcut;
    }

    void CControlUI::SetShortcut(TCHAR ch) {
        m_chShortcut = ch;
    }

    bool CControlUI::IsContextMenuUsed() const {
        return m_bMenuUsed;
    }

    void CControlUI::SetContextMenuUsed(bool bMenuUsed) {
        m_bMenuUsed = bMenuUsed;
    }

    const CDuiString &CControlUI::GetUserData() {
        return m_sUserData;
    }

    void CControlUI::SetUserData(LPCTSTR pstrText) {
        m_sUserData = pstrText;
    }

    UINT_PTR CControlUI::GetTag() const {
        return m_pTag;
    }

    void CControlUI::SetTag(UINT_PTR pTag) {
        m_pTag = pTag;
    }

    bool CControlUI::IsVisible() const {

        return m_bVisible && m_bInternVisible;
    }

    void CControlUI::SetVisible(bool bVisible) {
        if( m_bVisible == bVisible ) return;

        bool v = IsVisible();
        m_bVisible = bVisible;

        if( m_bFocused ) m_bFocused = false;

        if (!bVisible && m_pManager && m_pManager->GetFocus() == this) {
            m_pManager->SetFocus(NULL) ;
        }

        if( IsVisible() != v ) {
            NeedParentUpdate();
        }
    }

    void CControlUI::SetInternVisible(bool bVisible) {
        m_bInternVisible = bVisible;

        if (!bVisible && m_pManager && m_pManager->GetFocus() == this) {
            m_pManager->SetFocus(NULL) ;
        }
    }

    bool CControlUI::IsEnabled() const {
        return m_bEnabled;
    }

    void CControlUI::SetEnabled(bool bEnabled) {
        if( m_bEnabled == bEnabled ) return;

        m_bEnabled = bEnabled;
        Invalidate();
    }

    bool CControlUI::IsMouseEnabled() const {
        return m_bMouseEnabled;
    }

    void CControlUI::SetMouseEnabled(bool bEnabled) {
        m_bMouseEnabled = bEnabled;
    }

    bool CControlUI::IsKeyboardEnabled() const {
        return m_bKeyboardEnabled ;
    }
    void CControlUI::SetKeyboardEnabled(bool bEnabled) {
        m_bKeyboardEnabled = bEnabled ;
    }

    bool CControlUI::IsFocused() const {
        return m_bFocused;
    }

    void CControlUI::SetFocus() {
        if( m_pManager != NULL ) m_pManager->SetFocus(this);
    }

    bool CControlUI::IsFloat() const {
        return m_bFloat;
    }

    void CControlUI::SetFloat(bool bFloat) {
        if( m_bFloat == bFloat ) return;

        m_bFloat = bFloat;
        NeedParentUpdate();
    }

    CControlUI *CControlUI::FindControl(FINDCONTROLPROC Proc, LPVOID pData, UINT uFlags) {
        if( (uFlags & UIFIND_VISIBLE) != 0 && !IsVisible() ) return NULL;

        if( (uFlags & UIFIND_ENABLED) != 0 && !IsEnabled() ) return NULL;

        if( (uFlags & UIFIND_HITTEST) != 0 && (!m_bMouseEnabled || !::PtInRect(&m_rcItem, * static_cast<LPPOINT>(pData))) ) return NULL;

        return Proc(this, pData);
    }

    void CControlUI::Invalidate() {
        if( !IsVisible() ) return;

        RECT invalidateRc = m_rcItem;

        CControlUI *pParent = this;
        RECT rcTemp;
        RECT rcParent;

        while( (pParent = pParent->GetParent()) ) {
            rcTemp = invalidateRc;
            rcParent = pParent->GetPos();

            if( !::IntersectRect(&invalidateRc, &rcTemp, &rcParent) ) {
                return;
            }
        }

        if (m_pManager != NULL) {
            m_pManager->Invalidate(invalidateRc);
        }
    }

    bool CControlUI::IsUpdateNeeded() const {
        return m_bUpdateNeeded;
    }

    void CControlUI::NeedUpdate() {
        if( !IsVisible() ) return;

        m_bUpdateNeeded = true;
        Invalidate();

        if( m_pManager != NULL ) m_pManager->NeedUpdate();
    }

    void CControlUI::NeedParentUpdate() {
        if( GetParent() ) {
            GetParent()->NeedUpdate();
            GetParent()->Invalidate();
        } else {
            NeedUpdate();
        }

        if( m_pManager != NULL ) m_pManager->NeedUpdate();
    }

    DWORD CControlUI::GetAdjustColor(DWORD dwColor) {
        if( !m_bColorHSL )
            return dwColor;

        short H, S, L;
        CPaintManagerUI::GetHSL(&H, &S, &L);
        return CRenderEngine::AdjustColor(dwColor, H, S, L);
    }

    void CControlUI::Init() {
        DoInit();

        if( m_OnInitDelegate )
            m_OnInitDelegate(this);
    }

    void CControlUI::DoInit() {

    }

    void CControlUI::Event(TEventUI &event) {
        m_OnEventDelegate(&event);
        DoEvent(event);
    }

    void CControlUI::DoEvent(TEventUI &event) {
        if( event.Type == UIEVENT_SETCURSOR ) {
            if( GetCursor() ) {
                ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(GetCursor())));
            } else {
                ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)));
            }
        } else if( event.Type == UIEVENT_SETFOCUS ) {
            m_bFocused = true;
            Invalidate();
        } else if( event.Type == UIEVENT_KILLFOCUS ) {
            m_bFocused = false;
            Invalidate();
        } else if( event.Type == UIEVENT_TIMER ) {
            m_pManager->SendNotify(this, DUI_MSGTYPE_TIMER, event.wParam, event.lParam);
        } else if( event.Type == UIEVENT_CONTEXTMENU ) {
            if( IsContextMenuUsed() ) {
                m_pManager->SendNotify(this, DUI_MSGTYPE_MENU, event.wParam, event.lParam);
            }
        } else {
            if (m_pParent != NULL)
                m_pParent->DoEvent(event);
        }
    }


    void CControlUI::SetVirtualWnd(LPCTSTR pstrValue) {
        m_sVirtualWnd = pstrValue;
        m_pManager->UsedVirtualWnd(true);
    }

    CDuiString CControlUI::GetVirtualWnd() const {
        CDuiString str;

        if( !m_sVirtualWnd.IsEmpty() ) {
            str = m_sVirtualWnd;
        } else {
            CControlUI *pParent = GetParent();

            if( pParent != NULL) {
                str = pParent->GetVirtualWnd();
            } else {
                str = _T("");
            }
        }

        return str;
    }

    void CControlUI::AddCustomAttribute(LPCTSTR pstrName, LPCTSTR pstrAttr) {
        if( pstrName == NULL || pstrName[0] == _T('\0') || pstrAttr == NULL || pstrAttr[0] == _T('\0') )
            return;

        CDuiString *pCostomAttr = new CDuiString(pstrAttr);

        if (pCostomAttr != NULL) {
            if (m_mCustomAttrHash.Find(pstrName) == NULL)
                m_mCustomAttrHash.Set(pstrName, (LPVOID)pCostomAttr);
            else
                delete pCostomAttr;
        }
    }

    LPCTSTR CControlUI::GetCustomAttribute(LPCTSTR pstrName) const {
        if( pstrName == NULL || pstrName[0] == _T('\0') )
            return NULL;

        CDuiString *pCostomAttr = static_cast<CDuiString *>(m_mCustomAttrHash.Find(pstrName));

        if( pCostomAttr )
            return pCostomAttr->GetData();

        return NULL;
    }

    bool CControlUI::RemoveCustomAttribute(LPCTSTR pstrName) {
        if( pstrName == NULL || pstrName[0] == _T('\0') )
            return NULL;

        CDuiString *pCostomAttr = static_cast<CDuiString *>(m_mCustomAttrHash.Find(pstrName));

        if( !pCostomAttr )
            return false;

        delete pCostomAttr;
        return m_mCustomAttrHash.Remove(pstrName);
    }

    void CControlUI::RemoveAllCustomAttribute() {
        CDuiString *pCostomAttr;

        for (std::map<CDuiString, LPVOID>::iterator it = m_mCustomAttrHash.Begin(); it != m_mCustomAttrHash.End(); it++) {
            pCostomAttr = static_cast<CDuiString *>(it->second);
            if(pCostomAttr)
                delete pCostomAttr;
        }

        m_mCustomAttrHash.RemoveAll();
    }

    void CControlUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) {
        // 是否样式表
        if(m_pManager != NULL) {
            LPCTSTR pStyle = m_pManager->GetStyle(pstrValue);

            if( pStyle != NULL) {
                ApplyAttributeList(pStyle);
                return;
            }
        }

        if( _tcsicmp(pstrName, _T("pos")) == 0 ) {
            RECT rcPos = { 0 };
            LPTSTR pstr = NULL;
            rcPos.left = _tcstol(pstrValue, &pstr, 10);
            ASSERT(pstr);
            rcPos.top = _tcstol(pstr + 1, &pstr, 10);
            ASSERT(pstr);
            rcPos.right = _tcstol(pstr + 1, &pstr, 10);
            ASSERT(pstr);
            rcPos.bottom = _tcstol(pstr + 1, &pstr, 10);
            ASSERT(pstr);
            SIZE szXY = {rcPos.left >= 0 ? rcPos.left : rcPos.right, rcPos.top >= 0 ? rcPos.top : rcPos.bottom};
            SetFixedXY(szXY);
            SetFixedWidth(rcPos.right - rcPos.left);
            SetFixedHeight(rcPos.bottom - rcPos.top);
        } else if( _tcsicmp(pstrName, _T("float")) == 0 ) {
            CDuiString nValue = pstrValue;

            // 动态计算相对比例
            if(nValue.Find(',') < 0) {
                SetFloat(_tcsicmp(pstrValue, _T("true")) == 0);
            } else {
                TPercentInfo piFloatPercent = { 0 };
                LPTSTR pstr = NULL;
                piFloatPercent.left = _tcstod(pstrValue, &pstr);
                ASSERT(pstr);
                piFloatPercent.top = _tcstod(pstr + 1, &pstr);
                ASSERT(pstr);
                piFloatPercent.right = _tcstod(pstr + 1, &pstr);
                ASSERT(pstr);
                piFloatPercent.bottom = _tcstod(pstr + 1, &pstr);
                ASSERT(pstr);
                SetFloatPercent(piFloatPercent);
                SetFloat(true);
            }
        } else if( _tcsicmp(pstrName, _T("floatalign")) == 0) {
            UINT uAlign = GetFloatAlign();

            // 解析文字属性
            while( *pstrValue != _T('\0') ) {
                CDuiString sValue;

                while( *pstrValue == _T(',') || *pstrValue == _T(' ') )
                    pstrValue = ::CharNext(pstrValue);

                while( *pstrValue != _T('\0') && *pstrValue != _T(',') && *pstrValue != _T(' ') ) {
                    LPTSTR pstrTemp = ::CharNext(pstrValue);

                    while( pstrValue < pstrTemp) {
                        sValue += *pstrValue++;
                    }
                }

                if(sValue.CompareNoCase(_T("null")) == 0) {
                    uAlign = 0;
                }

                if( sValue.CompareNoCase(_T("left")) == 0 ) {
                    uAlign &= ~(DT_CENTER | DT_RIGHT);
                    uAlign |= DT_LEFT;
                } else if( sValue.CompareNoCase(_T("center")) == 0 ) {
                    uAlign &= ~(DT_LEFT | DT_RIGHT);
                    uAlign |= DT_CENTER;
                } else if( sValue.CompareNoCase(_T("right")) == 0 ) {
                    uAlign &= ~(DT_LEFT | DT_CENTER);
                    uAlign |= DT_RIGHT;
                } else if( sValue.CompareNoCase(_T("top")) == 0 ) {
                    uAlign &= ~(DT_BOTTOM | DT_VCENTER);
                    uAlign |= DT_TOP;
                } else if( sValue.CompareNoCase(_T("vcenter")) == 0 ) {
                    uAlign &= ~(DT_TOP | DT_BOTTOM);
                    uAlign |= DT_VCENTER;
                } else if( sValue.CompareNoCase(_T("bottom")) == 0 ) {
                    uAlign &= ~(DT_TOP | DT_VCENTER);
                    uAlign |= DT_BOTTOM;
                }
            }

            SetFloatAlign(uAlign);
        } else if( _tcsicmp(pstrName, _T("padding")) == 0 ) {
            RECT rcPadding = { 0 };
            LPTSTR pstr = NULL;
            rcPadding.left = _tcstol(pstrValue, &pstr, 10);
            ASSERT(pstr);
            rcPadding.top = _tcstol(pstr + 1, &pstr, 10);
            ASSERT(pstr);
            rcPadding.right = _tcstol(pstr + 1, &pstr, 10);
            ASSERT(pstr);
            rcPadding.bottom = _tcstol(pstr + 1, &pstr, 10);
            ASSERT(pstr);
            SetPadding(rcPadding);
        } else if( _tcsicmp(pstrName, _T("gradient")) == 0 )
            SetGradient(pstrValue);
        else if( _tcsicmp(pstrName, _T("bkcolor")) == 0 || _tcsicmp(pstrName, _T("bkcolor1")) == 0 ) {
            while( *pstrValue > _T('\0') && *pstrValue <= _T(' ') ) pstrValue = ::CharNext(pstrValue);

            if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue);

            LPTSTR pstr = NULL;
            DWORD clrColor = _tcstoul(pstrValue, &pstr, 16);
            SetBkColor(clrColor);
        } else if( _tcsicmp(pstrName, _T("bkcolor2")) == 0 ) {
            while( *pstrValue > _T('\0') && *pstrValue <= _T(' ') ) pstrValue = ::CharNext(pstrValue);

            if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue);

            LPTSTR pstr = NULL;
            DWORD clrColor = _tcstoul(pstrValue, &pstr, 16);
            SetBkColor2(clrColor);
        } else if( _tcsicmp(pstrName, _T("bkcolor3")) == 0 ) {
            while( *pstrValue > _T('\0') && *pstrValue <= _T(' ') ) pstrValue = ::CharNext(pstrValue);

            if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue);

            LPTSTR pstr = NULL;
            DWORD clrColor = _tcstoul(pstrValue, &pstr, 16);
            SetBkColor3(clrColor);
        } else if( _tcsicmp(pstrName, _T("forecolor")) == 0 ) {
            while( *pstrValue > _T('\0') && *pstrValue <= _T(' ') ) pstrValue = ::CharNext(pstrValue);

            if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue);

            LPTSTR pstr = NULL;
            DWORD clrColor = _tcstoul(pstrValue, &pstr, 16);
            SetForeColor(clrColor);
        } else if( _tcsicmp(pstrName, _T("bordercolor")) == 0 ) {
            if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue);

            LPTSTR pstr = NULL;
            DWORD clrColor = _tcstoul(pstrValue, &pstr, 16);
            SetBorderColor(clrColor);
        } else if( _tcsicmp(pstrName, _T("focusbordercolor")) == 0 ) {
            if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue);

            LPTSTR pstr = NULL;
            DWORD clrColor = _tcstoul(pstrValue, &pstr, 16);
            SetFocusBorderColor(clrColor);
        } else if (_tcsicmp(pstrName, _T("colorhsl")) == 0) {
            SetColorHSL(_tcsicmp(pstrValue, _T("true")) == 0);
        } else if( _tcsicmp(pstrName, _T("bordersize")) == 0 ) {
            CDuiString nValue = pstrValue;

            if(nValue.Find(',') < 0) {
                int iValue = _ttoi(pstrValue);
                RECT rcPadding = { iValue, iValue, iValue, iValue };
                SetBorderSize(rcPadding);
            } else {
                RECT rcPadding = { 0 };
                LPTSTR pstr = NULL;
                rcPadding.left = _tcstol(pstrValue, &pstr, 10);
                ASSERT(pstr);
                rcPadding.top = _tcstol(pstr + 1, &pstr, 10);
                ASSERT(pstr);
                rcPadding.right = _tcstol(pstr + 1, &pstr, 10);
                ASSERT(pstr);
                rcPadding.bottom = _tcstol(pstr + 1, &pstr, 10);
                ASSERT(pstr);
                SetBorderSize(rcPadding);
            }
        } else if( _tcsicmp(pstrName, _T("borderstyle")) == 0 )
            SetBorderStyle(_ttoi(pstrValue));
        else if( _tcsicmp(pstrName, _T("borderround")) == 0 ) {
            SIZE cxyRound = { 0 };
            LPTSTR pstr = NULL;
            cxyRound.cx = _tcstol(pstrValue, &pstr, 10);
            ASSERT(pstr);
            cxyRound.cy = _tcstol(pstr + 1, &pstr, 10);
            ASSERT(pstr);
            SetBorderRound(cxyRound);
        } else if( _tcsicmp(pstrName, _T("bkimage")) == 0 )
            SetBkImage(pstrValue);
        else if( _tcsicmp(pstrName, _T("foreimage")) == 0 )
            SetForeImage(pstrValue);
        else if( _tcsicmp(pstrName, _T("width")) == 0 )
            SetFixedWidth(_ttoi(pstrValue));
        else if( _tcsicmp(pstrName, _T("height")) == 0 )
            SetFixedHeight(_ttoi(pstrValue));
        else if( _tcsicmp(pstrName, _T("minwidth")) == 0 )
            SetMinWidth(_ttoi(pstrValue));
        else if( _tcsicmp(pstrName, _T("minheight")) == 0 )
            SetMinHeight(_ttoi(pstrValue));
        else if( _tcsicmp(pstrName, _T("maxwidth")) == 0 )
            SetMaxWidth(_ttoi(pstrValue));
        else if( _tcsicmp(pstrName, _T("maxheight")) == 0 )
            SetMaxHeight(_ttoi(pstrValue));
        else if( _tcsicmp(pstrName, _T("name")) == 0 )
            SetName(pstrValue);
        else if( _tcsicmp(pstrName, _T("drag")) == 0 )
            SetDragEnable(_tcsicmp(pstrValue, _T("true")) == 0);
        else if( _tcsicmp(pstrName, _T("drop")) == 0 )
            SetDropEnable(_tcsicmp(pstrValue, _T("true")) == 0);
        else if( _tcsicmp(pstrName, _T("resourcetext")) == 0 )
            SetResourceText(_tcsicmp(pstrValue, _T("true")) == 0);
        else if( _tcsicmp(pstrName, _T("text")) == 0 )
            SetText(pstrValue);
        else if( _tcsicmp(pstrName, _T("tooltip")) == 0 )
            SetToolTip(pstrValue);
        else if( _tcsicmp(pstrName, _T("userdata")) == 0 )
            SetUserData(pstrValue);
        else if( _tcsicmp(pstrName, _T("enabled")) == 0 )
            SetEnabled(_tcsicmp(pstrValue, _T("true")) == 0);
        else if( _tcsicmp(pstrName, _T("mouse")) == 0 )
            SetMouseEnabled(_tcsicmp(pstrValue, _T("true")) == 0);
        else if( _tcsicmp(pstrName, _T("keyboard")) == 0 )
            SetKeyboardEnabled(_tcsicmp(pstrValue, _T("true")) == 0);
        else if( _tcsicmp(pstrName, _T("visible")) == 0 )
            SetVisible(_tcsicmp(pstrValue, _T("true")) == 0);
        else if( _tcsicmp(pstrName, _T("shortcut")) == 0 )
            SetShortcut(pstrValue[0]);
        else if( _tcsicmp(pstrName, _T("menu")) == 0 )
            SetContextMenuUsed(_tcsicmp(pstrValue, _T("true")) == 0);
        else if( _tcsicmp(pstrName, _T("cursor")) == 0 && pstrValue) {
            if( _tcsicmp(pstrValue, _T("arrow")) == 0 )         SetCursor(DUI_ARROW);
            else if( _tcsicmp(pstrValue, _T("ibeam")) == 0 )    SetCursor(DUI_IBEAM);
            else if( _tcsicmp(pstrValue, _T("wait")) == 0 )     SetCursor(DUI_WAIT);
            else if( _tcsicmp(pstrValue, _T("cross")) == 0 )    SetCursor(DUI_CROSS);
            else if( _tcsicmp(pstrValue, _T("uparrow")) == 0 )  SetCursor(DUI_UPARROW);
            else if( _tcsicmp(pstrValue, _T("size")) == 0 )     SetCursor(DUI_SIZE);
            else if( _tcsicmp(pstrValue, _T("icon")) == 0 )     SetCursor(DUI_ICON);
            else if( _tcsicmp(pstrValue, _T("sizenwse")) == 0 ) SetCursor(DUI_SIZENWSE);
            else if( _tcsicmp(pstrValue, _T("sizenesw")) == 0 ) SetCursor(DUI_SIZENESW);
            else if( _tcsicmp(pstrValue, _T("sizewe")) == 0 )   SetCursor(DUI_SIZEWE);
            else if( _tcsicmp(pstrValue, _T("sizens")) == 0 )   SetCursor(DUI_SIZENS);
            else if( _tcsicmp(pstrValue, _T("sizeall")) == 0 )  SetCursor(DUI_SIZEALL);
            else if( _tcsicmp(pstrValue, _T("no")) == 0 )       SetCursor(DUI_NO);
            else if( _tcsicmp(pstrValue, _T("hand")) == 0 )     SetCursor(DUI_HAND);
        } else if( _tcsicmp(pstrName, _T("virtualwnd")) == 0 )
            SetVirtualWnd(pstrValue);
        else if( _tcsicmp(pstrName, _T("innerstyle")) == 0 ) {
            CDuiString sXmlData = pstrValue;
            sXmlData.Replace(_T("&quot;"), _T("\""));
            LPCTSTR pstrList = sXmlData;
            CDuiString sItem;
            CDuiString sValue;

            while( *pstrList != _T('\0') ) {
                sItem.Empty();
                sValue.Empty();

                while( *pstrList != _T('\0') && *pstrList != _T('=') ) {
                    LPTSTR pstrTemp = ::CharNext(pstrList);

                    while( pstrList < pstrTemp) {
                        sItem += *pstrList++;
                    }
                }

                ASSERT( *pstrList == _T('=') );

                if( *pstrList++ != _T('=') ) return;

                ASSERT( *pstrList == _T('\"') );

                if( *pstrList++ != _T('\"') ) return;

                while( *pstrList != _T('\0') && *pstrList != _T('\"') ) {
                    LPTSTR pstrTemp = ::CharNext(pstrList);

                    while( pstrList < pstrTemp) {
                        sValue += *pstrList++;
                    }
                }

                ASSERT( *pstrList == _T('\"') );

                if( *pstrList++ != _T('\"') ) return;

                SetAttribute(sItem, sValue);

                if( *pstrList++ != _T(' ') && *pstrList++ != _T(',') ) return;
            }
        } else {
            AddCustomAttribute(pstrName, pstrValue);
        }
    }

    CControlUI *CControlUI::ApplyAttributeList(LPCTSTR pstrValue) {
        // 解析样式表
        if(m_pManager != NULL) {
            LPCTSTR pStyle = m_pManager->GetStyle(pstrValue);

            if( pStyle != NULL) {
                return ApplyAttributeList(pStyle);
            }
        }

        CDuiString sXmlData = pstrValue;
        sXmlData.Replace(_T("&quot;"), _T("\""));
        LPCTSTR pstrList = sXmlData;
        // 解析样式属性
        CDuiString sItem;
        CDuiString sValue;

        while( *pstrList != _T('\0') ) {
            sItem.Empty();
            sValue.Empty();

            while( *pstrList != _T('\0') && *pstrList != _T('=') ) {
                LPTSTR pstrTemp = ::CharNext(pstrList);

                while( pstrList < pstrTemp) {
                    sItem += *pstrList++;
                }
            }

            ASSERT( *pstrList == _T('=') );

            if( *pstrList++ != _T('=') ) return this;

            ASSERT( *pstrList == _T('\"') );

            if( *pstrList++ != _T('\"') ) return this;

            while( *pstrList != _T('\0') && *pstrList != _T('\"') ) {
                LPTSTR pstrTemp = ::CharNext(pstrList);

                while( pstrList < pstrTemp) {
                    sValue += *pstrList++;
                }
            }

            ASSERT( *pstrList == _T('\"') );

            if( *pstrList++ != _T('\"') ) return this;

            SetAttribute(sItem, sValue);

            if( *pstrList++ != _T(' ') && *pstrList++ != _T(',') ) return this;
        }

        return this;
    }

    SIZE CControlUI::EstimateSize(SIZE szAvailable) {
        if (!m_pManager)
            return m_cxyFixed;
        return m_pManager->GetDPIObj()->Scale(m_cxyFixed);
    }

    bool CControlUI::Paint(HDC hDC, const RECT &rcPaint, CControlUI *pStopControl) {
        if (pStopControl == this)
            return false;

        if( !::IntersectRect(&m_rcPaint, &rcPaint, &m_rcItem) )
            return true;

        if (!DoPaint(hDC, m_rcPaint, pStopControl))
            return false;

        return true;
    }

    bool CControlUI::DoPaint(HDC hDC, const RECT &rcPaint, CControlUI *pStopControl) {
        // 绘制循序：背景颜色->背景图->状态图->文本->边框
        SIZE cxyBorderRound;
        RECT rcBorderSize;

        if (m_pManager) {
            cxyBorderRound = m_pManager->GetDPIObj()->Scale(m_cxyBorderRound);
            rcBorderSize = m_pManager->GetDPIObj()->Scale(m_rcBorderSize);
        } else {
            cxyBorderRound = m_cxyBorderRound;
            rcBorderSize = m_rcBorderSize;
        }

        if( cxyBorderRound.cx > 0 || cxyBorderRound.cy > 0 ) {
            CRenderClip roundClip;
            CRenderClip::GenerateRoundClip(hDC, m_rcPaint,  m_rcItem, cxyBorderRound.cx, cxyBorderRound.cy, roundClip);
        }

        PaintBkColor(hDC);
        PaintBkImage(hDC);
        PaintStatusImage(hDC);
        PaintForeColor(hDC);
        PaintForeImage(hDC);
        PaintText(hDC);
        PaintBorder(hDC);

        return true;
    }

    void CControlUI::PaintBkColor(HDC hDC) {
        if( m_dwBackColor != 0 ) {
            bool bVer = (m_sGradient.CompareNoCase(_T("hor")) != 0);

            if( m_dwBackColor2 != 0 ) {
                if( m_dwBackColor3 != 0 ) {
                    RECT rc = m_rcItem;
                    rc.bottom = (rc.bottom + rc.top) / 2;
                    CRenderEngine::DrawGradient(hDC, rc, GetAdjustColor(m_dwBackColor), GetAdjustColor(m_dwBackColor2), bVer, 8);
                    rc.top = rc.bottom;
                    rc.bottom = m_rcItem.bottom;
                    CRenderEngine::DrawGradient(hDC, rc, GetAdjustColor(m_dwBackColor2), GetAdjustColor(m_dwBackColor3), bVer, 8);
                } else {
                    CRenderEngine::DrawGradient(hDC, m_rcItem, GetAdjustColor(m_dwBackColor), GetAdjustColor(m_dwBackColor2), bVer, 16);
                }
            } else if( m_dwBackColor >= 0xFF000000 )
                CRenderEngine::DrawColor(hDC, m_rcPaint, GetAdjustColor(m_dwBackColor));
            else
                CRenderEngine::DrawColor(hDC, m_rcItem, GetAdjustColor(m_dwBackColor));
        }
    }

    void CControlUI::PaintBkImage(HDC hDC) {
        if( m_sBkImage.IsEmpty() ) return;

        if( !DrawImage(hDC, (LPCTSTR)m_sBkImage) ) {
        }
    }

    void CControlUI::PaintStatusImage(HDC hDC) {
        return;
    }

    void CControlUI::PaintForeColor(HDC hDC) {
        CRenderEngine::DrawColor(hDC, m_rcItem, GetAdjustColor(m_dwForeColor));
    }

    void CControlUI::PaintForeImage(HDC hDC) {
        if( m_sForeImage.IsEmpty() )
            return;

        DrawImage(hDC, (LPCTSTR)m_sForeImage);
    }

    void CControlUI::PaintText(HDC hDC) {
        return;
    }

    void CControlUI::PaintBorder(HDC hDC) {
        SIZE cxyBorderRound = GetBorderRound();
        CDuiRect rcBorderSize = GetBorderSize();
        DWORD dwBorderColor = IsFocused() ? m_dwFocusBorderColor :  m_dwBorderColor;

        if (dwBorderColor == 0) {
            return;
        }

        RECT rcBorder;

        if (rcBorderSize.left > 0) {
            rcBorder = m_rcItem;
            rcBorder.right = rcBorder.left;
            CRenderEngine::DrawLine(hDC, rcBorder, rcBorderSize.left, GetAdjustColor(dwBorderColor), m_nBorderStyle);
        }

        if (rcBorderSize.top > 0) {
            rcBorder = m_rcItem;
            rcBorder.bottom = rcBorder.top;
            CRenderEngine::DrawLine(hDC, rcBorder, rcBorderSize.top, GetAdjustColor(dwBorderColor), m_nBorderStyle);
        }

        if (rcBorderSize.right > 0) {
            rcBorder = m_rcItem;
            rcBorder.left = rcBorder.right;
            CRenderEngine::DrawLine(hDC, rcBorder, rcBorderSize.right, GetAdjustColor(dwBorderColor), m_nBorderStyle);
        }

        if (rcBorderSize.bottom > 0) {
            rcBorder = m_rcItem;
            rcBorder.top = rcBorder.bottom;
            CRenderEngine::DrawLine(hDC, rcBorder, rcBorderSize.bottom, GetAdjustColor(dwBorderColor), m_nBorderStyle);
        }
    }

    void CControlUI::DoPostPaint(HDC hDC, const RECT &rcPaint) {
        return;
    }

    int CControlUI::GetBorderStyle() const {
        return m_nBorderStyle;
    }

    void CControlUI::SetBorderStyle( int nStyle ) {
        m_nBorderStyle = nStyle;
        Invalidate();
    }

} // namespace DuiLib
